package org.redr.canvas;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.util.Arrays;
import java.util.List;

import org.rosuda.REngine.REXP;
import org.rosuda.REngine.REXPLogical;
import org.rosuda.REngine.REXPMismatchException;
import org.rosuda.REngine.REXPVector;
import org.rosuda.REngine.Rserve.RConnection;
import org.rosuda.REngine.Rserve.RFileOutputStream;
import org.rosuda.REngine.Rserve.RserveException;

import com.trolltech.qt.gui.QMessageBox;

// String path = Test.class.getProtectionDomain().getCodeSource().getLocation().getPath();
// String decodedPath = URLDecoder.decode(path, "UTF-8");
public class RSession {
	
	static RConnection c;
	static Thread loop;
	static int Rport = 32211;
	
	public static void startUp() throws RserveException{
		
//		ServerSocket s;
//		try {
//			s = new ServerSocket(0);
//			Rport = s.getLocalPort();
//			System.out.println("setting port: " + Rport);
//			s.close();
//		} catch (IOException e1) {
//			// TODO Auto-generated catch block
//			e1.printStackTrace();
//		}
//		
//		RLoop rloop = new RLoop();
//		loop = new Thread(rloop);
//		loop.start();
////		c = loop.rconnection();
//		while (!rloop.started) {
//			try {
//				Thread.sleep(2000);
//			} catch (InterruptedException e) {
//				// TODO Auto-generated catch block
//				e.printStackTrace();
//			}
//		}
		System.out.println("Starting R session");
		// try{
		c = new RConnection("localhost", Rport);
//		} catch (Exception e) {
//			e.printStackTrace();
//			System.out.println("Waiting for rserve to start");
//			try {
//				Thread.sleep(3000);
//			} catch (InterruptedException e1) {
//				// TODO Auto-generated catch block
//				e1.printStackTrace();
//			}
//			c = new RConnection("localhost", Rport);
//		}
		System.out.println("R Session Started");
		
		try {
			REXP version = R("R.Version()$version.string");
			RedRLog.log("Testing connection to R, R Version follows");
			RedRLog.log(version.asString());
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	public static void shutDown(){
		try {
			c.shutdown();
			// c.detach();
			// c.serverShutdown();
		} catch (RserveException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}


	public static REXP safeEval(String s) throws RserveException, 
			RException, REXPMismatchException {
		System.out.println(s);
		REXP r = c.eval("try({" + s + "}, silent=TRUE)");
		if (r.inherits("try-error")) throw new RException(r.asString());
		return r;
	}
	
	public static void voidSafeEval(String s) throws RserveException, 
	RException, REXPMismatchException {
		
	}
			
	/**
	 * Run the specified command in the R server AND return the result.
	 * 
	 * @param s
	 * @return REXP
	 * @throws Exception
	 */
	public static REXP Reval(String s) throws Exception{
		// System.out.println(">>> " + s);
		System.out.println(s);
		return c.eval(s);
	}
	
	/**
	 * Run the specified command and return nothing.
	 * 
	 * @param s
	 * @throws Exception
	 */
	public static void RVoideval(String s) throws Exception{
		System.out.println(String.format("Evaluating %s", s));
		try{
			c.voidEval(s);
		} catch (Exception e){
			System.out.println(String.format("Failed to evaluate %s", s));
			e.printStackTrace();
			REXP error = c.eval(String.format("str(try(%s))", s));
			System.out.println(error.asString());
		}
	}
	
	/**
	 * Transfers the file specified by file to the remote server and then reads it into the vector vname.
	 * The existence of the file is first checked and if override is false then the existing file is used.
	 * The override feature saves IO time if the same file is to be checked more than once (for example if the user wanted to test various delim options or later wanted to do something with the file.
	 * 
	 * @param file
	 * @param vname
	 * @param override
	 * @throws Exception 
	 */
	public static void readDelim(String file, String vname, boolean override, String delimargs) throws Exception{
		if (delimargs.equals("")){
			delimargs = "sep = \"\\t\", header = TRUE, fill = TRUE";
		}
		File clientFile = new File(file);
		
		String rfilepath = "file.path(tempdir(), \"" + clientFile.getName() + "\")";
		
		if (override || !((REXPLogical) Reval("file.exists(" + rfilepath + ")")).isTRUE()[0]){
			RFileOutputStream fos = c.createFile(clientFile.getName());
			FileInputStream fis = new FileInputStream(file);
			while (fis.available() < 5000){
				byte[] b = new byte[5000];
				fis.read(b);
				fos.write(b);
			}
			byte[] bf = new byte[fis.available()];
			fis.read(bf);
			fos.write(bf);
			fis.close();
			fos.close();
		}
		Reval(vname + "<-read.table(\"" + clientFile.getName() + "\", " + delimargs + ")");
	}
	
	private static class RLoop implements Runnable{
		// static RConnection c;
		public volatile boolean started = false;
		@Override
		public void run() {
			String command;
			if (System.getProperty("os.name").toLowerCase().contains("win")){
				command = String.format("\"%s/Rscript.exe\" -e \"require(Rserve); Rserve(port = %d)\"", "C:\\Program Files\\R\\R-2.15.0\\bin", Rport);
				//command =  "C:\\Users\\Spunky\\Documents\\R\\win-library\\2.13\\Rserve\\Rserve.exe --no-save"; // "C:\\Users\\Spunky\\Documents\\R\\win-library\\2.11\\Rserve\\Rserve.exe --no-save";
			} else {
				command = "R CMD Rserve --no-save";
			}
			System.out.println("Starting R Server: " + command);
			try {
				
				
				Process proc = Runtime.getRuntime().exec(command);
//				InputStream output = proc.getInputStream();
//				InputStreamReader isr = new InputStreamReader(output);
//				BufferedReader br = new BufferedReader(isr);
//				String line;
//				while ((line = br.readLine()) != null){
//					System.out.println(line);
//				}
				started = true;
				
//				Process proc2 = Runtime.getRuntime().exec("telnet localhost 6311");
//				InputStream output2 = proc2.getInputStream();
//				InputStreamReader isr2 = new InputStreamReader(output2);
//				BufferedReader br2 = new BufferedReader(isr2);
//				String line2;
//				while ((line2 = br2.readLine()) != null){
//					System.out.println(line2);
//				}
				
				// c = new RConnection();
				System.out.println("R Server Started");
			} catch (Exception e) {
				System.out.println("Failed to load R session.");
				e.printStackTrace();
			}
			
			
		}
		
	}
	
	/*
	static {
		try {
			//setLibPaths(RedREnviron.directoryNames.get("RlibPath"));
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	*/
	
	public static REXP R(String s) throws Exception
	{
		RedRLog.log(RedRLog.r, RedRLog.debug, s);
		try{
			REXP x;
			x = c.eval(s);
			return x;
		} catch (Exception e){
			RedRLog.log(RedRLog.r, RedRLog.warning, s + e.toString());
			throw e;
		}
	}
	
	/*
	public static void assign(String s, boolean[] o){
		c.assign(s, o);
	}
	
	public static void assign(String s, String[] o){
		re.assign(s, o);
	}
	
	public static void assign(String s, REXP o){
		re.assign(s, o);
	}
	
	public static void assign(String s, int[] o){
		re.assign(s, o);
	}
	
	public static void assign(String s, double[] o){
		re.assign(s, o);
	}
	*/
	
	public static void setLibPaths(File path) throws Exception{
		R(".libPaths(\""+path.getAbsolutePath()+"\")");
	}
	
	public static List<String> getInstalledPackages() throws Exception{
		REXPVector packages = (REXPVector) R("installed.packages()[,1]");
		// REXPVector pv	=	packages.asVector();
		String[] packagesList = packages.asStrings() ;
		return Arrays.asList(packagesList);
	}
	
	public static void require_library(String library) throws Exception{
		List<String> installedPackages	=	getInstalledPackages();
		if (installedPackages.contains(library)) return;
		install_library(library);
		if (getInstalledPackages().contains(library)) return;
		else throw new Exception("Library Not Found");
	}
	
	public static void install_library(String library) throws Exception{
		QMessageBox.StandardButtons buttons	=	new QMessageBox.StandardButtons();
		buttons.set(QMessageBox.StandardButton.Ok);
		buttons.set(QMessageBox.StandardButton.Cancel);
		
		QMessageBox.StandardButton ret = QMessageBox.information(null,
				"Download R Library", 
				"You are missing some key files for this widget or package.\nYou must install these files to use the widget or package you requested.\nIf you click No or Cancel you will likely see many errors.\nIf you are loading a package the package loading will be aborted.\n\n%s\n\nWould you like to download the Files (Hint: say Yes)?", 
				buttons, QMessageBox.StandardButton.Ok);
		
		
		if (ret == QMessageBox.StandardButton.Ok){
			try{
				R("setRepositories(ind=1:7)");
				R("install.packages(\""+library+"\")");
			} catch (Exception e) {
				throw e;
			}
		}
		
	}
	
	public static void updatePackages() throws Exception{
		R("update.packages(ask=F)");
	}
}
